#region Copyright Notice

// 
// 
//  
//  Copyright (c) 2009-2011 Anindya Chatterjee
//  
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//  
//      http://www.apache.org/licenses/LICENSE-2.0
//  
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// 
// 

#endregion

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.Win32;
using Winista.Mime;

namespace AbstractClass.IO
{
    /// <summary>
    /// Provides a set of static methods for common file system tasks.
    /// </summary>
    public static class FileSystem
    {
        /// <summary>
        /// Gets the MIME (Multipurpose Internet Mail Extensions) type of a file.
        /// </summary>
        /// <param name="filePath">Path of the file.</param>
        /// <returns>MIME type of the specified file.</returns>
        [SecurityPermission(SecurityAction.LinkDemand)]
        public static string GetFileMimeType(string filePath)
        {
            if (!File.Exists(filePath))
                throw new FileNotFoundException(filePath + LanguageResource.FileSystemGetFileMimeTypeNotFound);

            var buffer = new byte[256];

            using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            {
                var data = new byte[fs.Length];
                fs.Read(data, 0, (Int32) fs.Length);
                sbyte[] fileData = SupportUtil.ToSByteArray(data);
                var mimeTypes = new MimeTypes();
                MimeType mimeType = mimeTypes.GetMimeType(fileData);
                if (mimeType != null)
                    return mimeType.Name;

                if (fs.Length >= 256)
                    fs.Read(buffer, 0, 256);
                else
                    fs.Read(buffer, 0, (int) fs.Length);
            }

            try
            {
                UInt32 mimetype;
                NativeMethods.FindMimeFromData(IntPtr.Zero, null, buffer, 256, null, 0, out mimetype, 0);
                var mimeTypePtr = new IntPtr(mimetype);
                string mime = Marshal.PtrToStringUni(mimeTypePtr);
                Marshal.FreeCoTaskMem(mimeTypePtr);
                return mime;
            }
            catch (Exception)
            {
                return "unknown/unknown";
            }
        }

        /// <summary>
        /// Gets detailed information of all the registered file types in the system.
        /// </summary>
        /// <returns>A list of <see cref="FileTypeInfo"/> objects containing information about all registered file types.</returns>
        [SecurityPermission(SecurityAction.LinkDemand)]
        public static List<FileTypeInfo> GetRegisteredFiles()
        {
            RegistryKey rkRoot = Registry.ClassesRoot;
            string[] keyNames = rkRoot.GetSubKeyNames();
            var associationInfo = new List<FileTypeInfo>();

            foreach (string keyName in keyNames)
            {
                if (String.IsNullOrEmpty(keyName))
                    continue;
                int indexOfPoint = keyName.IndexOf(".");

                if (indexOfPoint != 0)
                    continue;

                RegistryKey rkFileType = rkRoot.OpenSubKey(keyName);
                if (rkFileType == null)
                    continue;

                object defaultValue = rkFileType.GetValue("");
                if (defaultValue == null)
                    continue;

                string defaultIcon = defaultValue + "\\DefaultIcon";
                RegistryKey rkFileIcon = rkRoot.OpenSubKey(defaultIcon);
                if (rkFileIcon != null)
                {
                    object value = rkFileIcon.GetValue("");
                    if (value != null)
                    {
                        string fileParam = value.ToString().Replace("\"", "");

                        var shinfo = new SHFILEINFOW();
                        NativeMethods.SHGetFileInfoW(keyName, 0, ref shinfo, (uint) Marshal.SizeOf(shinfo),
                                                     FileInfoOption.TypeName | FileInfoOption.UseFileAttributes);

                        var fileTypeInfo = new FileTypeInfo
                                               {
                                                   FileExtension = keyName,
                                                   IconInfo = GetFileIconInfo(fileParam),
                                                   FileType = Convert.ToString(shinfo.szTypeName.Trim())
                                               };
                        associationInfo.Add(fileTypeInfo);
                    }
                    rkFileIcon.Close();
                }
                rkFileType.Close();
            }
            rkRoot.Close();
            return associationInfo;
        }

        private static FileIconInfo GetFileIconInfo(string fileParam)
        {
            var fileIconInfo = new FileIconInfo();

            if (String.IsNullOrEmpty(fileParam))
                return fileIconInfo;

            string fileName;
            int iconIndex = 0;
            string iconIndexString = String.Empty;

            int commaIndex = fileParam.LastIndexOf(",");
            if (commaIndex > 0)
            {
                fileName = fileParam.Substring(0, commaIndex);
                iconIndexString = fileParam.Substring(commaIndex + 1);
            }
            else
                fileName = fileParam;

            if (!String.IsNullOrEmpty(iconIndexString))
            {
                iconIndex = int.Parse(iconIndexString);
                if (iconIndex < 0)
                    iconIndex = 0;
            }

            fileIconInfo.IconSource = fileName;
            fileIconInfo.IconIndex = iconIndex;

            return fileIconInfo;
        }

        /// <summary>
        /// Gets checksum of a file.
        /// </summary>
        /// <param name="filePath">The file path.</param>
        /// <param name="checksumType">Type of the checksum.</param>
        /// <returns>Checksum of the specified file.</returns>
        public static string GetFileHash(string filePath, ChecksumType checksumType)
        {
            string checksum;
            if (!File.Exists(filePath))
                throw new FileNotFoundException(LanguageResource.FileSystemGetFileHashSpecifiedFileNotFound, filePath);
            using (FileStream stream = File.OpenRead(filePath))
            {
                checksum = ChecksumCalculator.GetChecksum(stream, checksumType);
            }
            return checksum;
        }


        /// <summary>
        /// Determines whether the file name is legal.
        /// </summary>
        /// <param name="filePath">Name of the file.</param>
        /// <returns>
        /// 	<c>true</c> if file name is legal; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsValidFileName(string filePath)
        {
            if (string.IsNullOrEmpty(filePath) || filePath.Length >= 260)
            {
                return false;
            }
            // platform independent : check for invalid path chars
            if (Path.GetInvalidPathChars().Any(invalidChar => filePath.IndexOf(invalidChar) >= 0))
            {
                return false;
            }
            // platform dependent : Check for invalid file names (DOS)
            // this routine checks for following bad file names :
            // CON, PRN, AUX, NUL, COM1-9 and LPT1-9
            string nameWithoutExtension =
                Path.GetFileNameWithoutExtension(filePath);
            if (nameWithoutExtension != null)
            {
                nameWithoutExtension = nameWithoutExtension.ToUpper();
            }
            if (nameWithoutExtension == "CON" ||
                nameWithoutExtension == "PRN" ||
                nameWithoutExtension == "AUX" ||
                nameWithoutExtension == "NUL")
            {
                return false;
            }
            if (nameWithoutExtension != null)
            {
                char ch = nameWithoutExtension.Length == 4 ? nameWithoutExtension[3] : '\0';
                return !((nameWithoutExtension.StartsWith("COM") ||
                          nameWithoutExtension.StartsWith("LPT")) &&
                         Char.IsDigit(ch));
            }
            return true;
        }
    }
}